//
// Created on 2024/4/19.
//
// Node APIs are not fully supported. To solve the compilation error of the interface cannot be found,
// please include "napi/native_api.h".

#include "glesmain.h"
#include "mlog.h"
#include "thread/thread.h"
#include <bits/alltypes.h>
#include <native_vsync/native_vsync.h>
#include <string>
#include <ace/xcomponent/native_interface_xcomponent.h>
#include <unistd.h>

#include "thread_gfx.h"
#include "thread_loader.h"

const EGLint ATTRIB_LIST[] = {
    // Key,value.
    EGL_SURFACE_TYPE, EGL_WINDOW_BIT, EGL_RED_SIZE, 8, EGL_GREEN_SIZE, 8, EGL_BLUE_SIZE, 8, EGL_ALPHA_SIZE, 8,
    EGL_RENDERABLE_TYPE, EGL_OPENGL_ES3_BIT,
    // End.
    EGL_NONE};
const EGLint ATTRIB_LIST2[] = {
    // Key,value.
    EGL_SURFACE_TYPE, EGL_WINDOW_BIT, EGL_RED_SIZE, 8, EGL_GREEN_SIZE, 8, EGL_BLUE_SIZE, 8, EGL_ALPHA_SIZE, 8,
    EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
    // End.
    EGL_NONE};

static glesmain *_gthis = nullptr;


static std::atomic_bool _init(false);
void glesmain::OnSurfaceCreateCB(void *component, void *window) {
    _gthis = this;
    auto t = getThreadID();
    MLOGFormat("SurfaceCreate thread=%{public}16x", t);


    char idStr[OH_XCOMPONENT_ID_LEN_MAX + 1] = {'\0'};
    uint64_t idSize = OH_XCOMPONENT_ID_LEN_MAX + 1;
    if (OH_NativeXComponent_GetXComponentId((OH_NativeXComponent *)component, idStr, &idSize) !=
        OH_NATIVEXCOMPONENT_RESULT_SUCCESS) {
        MERROR("OnSurfaceCreatedCB: Unable to get XComponent id");
        return;
    }
    MLOGFormat("OH_NativeXComponent name=%{public}s", idStr);


    int32_t xSize =
        OH_NativeXComponent_GetXComponentSize((OH_NativeXComponent *)component, window, &this->width, &this->height);
    MLOGFormat("size thread=%{public}d %{public}d", width, height);

    MLOG("EglContextInit execute");
    if ((window == nullptr) || (width <= 0) || (height <= 0)) {
        MERROR("EglContextInit: param error");
        return;
    }
    m_egl_window = (EGLNativeWindowType)(window);

    //    // Init display.
    m_eglDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY);
    if (m_eglDisplay == EGL_NO_DISPLAY) {
        MERROR("eglGetDisplay: unable to get EGL display");
        return;
    }
    //
    {
        EGLint majorVersion;
        EGLint minorVersion;
        if (!eglInitialize(m_eglDisplay, &majorVersion, &minorVersion)) {
            MERROR("eglInitialize: unable to get initialize EGL display");
            return;
        }

        int err = eglGetError();
        MLOGFormat("eglInitialize %{public}d %{public}d err %{public}04x", majorVersion, minorVersion, err);
    }
    //    // Select configuration.
    EGLint numConfigs;
    { // 研究数量


        if (!eglGetConfigs(m_eglDisplay, nullptr, 0, &numConfigs)) {
            MERROR("eglChooseConfig: unable to choose configs");
            return;
        }
        MLOGFormat("eglChooseConfig max= %{public}d ", numConfigs);

        EGLConfig *configs = new EGLConfig[numConfigs];
        for (int i = 0; i < numConfigs; i++) {
            configs[i] = 0;
        }

        int num = numConfigs;
        if (!eglGetConfigs(m_eglDisplay, configs, num, &numConfigs)) {
            MERROR("eglChooseConfig: unable to choose configs");
            return;
        }
        for (int i = 0; i < numConfigs; i++) {
            MLOGFormat("eglChooseConfig %{public}d == %{public}d ", i, configs[i]);
        }


        delete[] configs;
    }
    bool bconfig = eglChooseConfig(m_eglDisplay, ATTRIB_LIST, &m_eglConfig, 1, &numConfigs);
    if (!bconfig) {
        MERROR("什么破设备GLES3都初始化不出来.");
        bconfig = eglChooseConfig(m_eglDisplay, ATTRIB_LIST2, &m_eglConfig, 1, &numConfigs);
        if (!bconfig) {
            MERROR("GLES2都初始化不出来,毁灭吧");
            return;
        }
    }
    // Create surface.
    int err2 = eglGetError();
    MLOGFormat("eglChooseConfig %{public}d %{public}04x", m_eglConfig, err2);
    EGLint winAttribs[] = {EGL_GL_COLORSPACE_KHR, EGL_GL_COLORSPACE_SRGB_KHR, EGL_NONE};
    m_eglSurface = eglCreateWindowSurface(m_eglDisplay, m_eglConfig, m_egl_window, nullptr);
    if (m_eglSurface == nullptr) {
        int err3 = eglGetError();
        // #define EGL_BAD_ATTRIBUTE 0x3004
        MERRORFormat("eglCreateWindowSurface %{public}d %{public}04x", m_eglSurface, err3);
        // MERROR("eglCreateWindowSurface: unable to create surface");
        return;
    }

    // 换个封装形式，不在主动创建线程
    //    Start_Thread_Gfx(this);
    //
    //    Start_Thread_Loader(this);
}
void glesmain::GFX_Init() {
    const EGLint CONTEXT_ATTRIBS[] = {EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE};
    m_eglContext_gfx = eglCreateContext(m_eglDisplay, m_eglConfig, EGL_NO_CONTEXT, CONTEXT_ATTRIBS);
    int err4 = eglGetError();
    MLOGFormat("eglCreateContext %{public}d %{public}04x", m_eglContext_gfx, err4);

    if (!eglMakeCurrent(m_eglDisplay, m_eglSurface, m_eglSurface, m_eglContext_gfx)) {
        int err = eglGetError();
        auto errstr = eglQueryString(m_eglDisplay, err);
        // 3006 EGL_BAD_CONTEXT
        MERRORFormat("eglMakeCurrent failed %{public}04x %{public}s", err, errstr);
        return;
    }

    const char *ptrver = (const char *)glGetString(GL_VERSION);
    MLOGFormat("===GLVersion %{public}s", ptrver);
    // Create program.
    //    program_ = CreateProgram(VERTEX_SHADER, FRAGMENT_SHADER);
    //    if (program_ == PROGRAM_ERROR) {
    //        MERROR("CreateProgram: unable to create program");
    //        return ;
    //    }
    glViewport(0, 0, width, height);
    _init = true;
}

bool glesmain::IsGFX_Init() {

    bool b = _init;

    return b;
}
void glesmain::Dispatch_OnInit() {

    napi_value func;
    napi_get_reference_value(this->cb_env, this->ref_oninit, &func);
    if (func == nullptr)
        return;
    {

        napi_value result;
        napi_call_function(cb_env, (napi_value) nullptr, (napi_value)func, (size_t)0, (const napi_value *)nullptr,
                           &result);
        return;
    }
}

// 垂直同步回调只需要发个信号，别的没你事儿
static std::thread *vsyncthread;

static sem_t vsync_sem;
static void OnSync(long long timestamp, void *data) {
    sem_post(&vsync_sem); // 发射信号
}

static void hm_threadsafe_func(napi_env env, napi_value js_fun, void *context, void *data) {
    {
        // 在arkts 的update
        size_t argc = 0;
        napi_value out_value = NULL;
        napi_status ret = napi_call_function(env, NULL, js_fun, argc, (const napi_value *)nullptr, &out_value);


        // finish and swapbuffer
        glFinish();
        eglSwapBuffers(_gthis->m_eglDisplay, _gthis->m_eglSurface);
    }
}
static void GFX_VSyncLoop() {
    std::string name = "render_vsync";
    auto syncobj = OH_NativeVSync_Create(name.c_str(), name.length());
    int v = 0;
    long lasttime = std::chrono::high_resolution_clock::now().time_since_epoch().count();
    while (true) {
        // 等待垂直同步信号
        // vsync_tag = false;
        OH_NativeVSync_RequestFrame(syncobj, OnSync, nullptr);

        sem_wait(&vsync_sem);

        long time = std::chrono::high_resolution_clock::now().time_since_epoch().count();
        float delta = (float)(time - lasttime) / 1000000.0f; // 换算成毫秒
        lasttime = time;
        // 计算帧间隔
        if (v % 100 == 0) {
            MLOGFormat("time 帧间隔=%{public}f ms", delta);
        }

        auto reg_output = (void *)(_gthis->Reg_Output);
        ((uint32_t *)reg_output)[0] = v;  // 帧ID
        ((float *)reg_output)[1] = delta; // Delta


        // Dispatch Update
        {
            if (_gthis->ref_onupdate == nullptr)
                continue;
            ;
            napi_value func = nullptr;
            napi_get_reference_value(_gthis->cb_env, _gthis->ref_onupdate, &func);
            if (func == nullptr)
                continue;
            ;
            napi_threadsafe_function tfunc;

            napi_status status = napi_create_threadsafe_function(_gthis->cb_env, (napi_value)func, (napi_value)NULL,
                                                                 (napi_value) "threadsafe_func", 0, 1, NULL, NULL, NULL,
                                                                 &hm_threadsafe_func, &tfunc);
            napi_call_threadsafe_function(tfunc, nullptr, napi_tsfn_blocking);
            napi_release_threadsafe_function(tfunc, napi_tsfn_release);
            v++;
        }
    }
}

void glesmain::StartVSyncThread() {
    _gthis = this;
    vsyncthread = new std::thread(GFX_VSyncLoop);
}
enum CMDType {
    None,
    CLEAR,
    MESH_CREATE,
    MESH_DESTORY,
    DRAWMESH,
};
void glesmain::Cmd_Commit() {
    CMDType type = (CMDType)(((uint32_t *)(this->Reg_Input))[0]);
    switch (type) {
    case CLEAR: {
        auto color = (float *)(this->Reg_Input + 4);
        glClearColor(color[0], color[1], color[2], color[3]);
        glClear(GL_COLOR_BUFFER_BIT);

    } break;
    case MESH_CREATE: {

        uint32_t vbosize = ((uint32_t *)(this->Reg_Input + 4))[0];
        uint32_t ebosize = ((uint32_t *)(this->Reg_Input + 8))[0];
            
        GLuint buf[2];
        glGenBuffers(2, buf);
        glBindBuffer(GL_ARRAY_BUFFER,buf[0]);
        glBufferData(GL_ARRAY_BUFFER,vbosize,nullptr,GL_DYNAMIC_DRAW);
            
        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER,buf[1]);
        glBufferData(GL_ELEMENT_ARRAY_BUFFER,ebosize,nullptr,GL_DYNAMIC_DRAW);
            
        auto reg_output = (void *)(_gthis->Reg_Output);
        ((uint32_t *)reg_output)[0] = buf[0]; //返回meshhandle
        ((uint32_t *)reg_output)[0] = buf[1]; // 返回meshhandle
    } break;
    default: {
        MLOGFormat("未知命令：%{public}d", type);
    }
    }
}
void glesmain::Cmd_Read() {}